------------------------------------------------------------------------------
	Developing standalone programs for use with SOLO
------------------------------------------------------------------------------

This document describes how to develop standalone programs with SOLO,
including simple standalone utilities, as well as full-fledged microkernels
and operating systems.

See the file 'notes' for more bits of misc useful info.

------------------------------------------------------------------------------
SOLO Program Execution, and srt0.o:

	When SOLO executes a program, it passes (in the 'ebx' register)
	the address of a 'BootParam' structure.  This structure contains
	information about the environment, as well as pointers to 
	basic I/O functions.    See the 'bootparam.h' file
	in 'lib/boot/m-pc' for a layout of this structure.

	The stack pointer still has the previous stack pointer
	from the 16-bit world, which means that less than 256 bytes
	of stack exist.  This stack pointer should probably not
	even be used, and should be replaced as soon as possible.

	'srt0.o' is linked to the beginning of all standalone executables
	for SOLO, so that when the program begins execution, the 
	pointer to the BootParam structure is saved in the global 
	variable 'bootparam' (of type BootParam*), the stack is
	initialized, and global constructors are called.

	By default, a 64K stack is allocated from the top of available
	virtual memory.  This size can be overridden by declaring
	a global variable of type 'size_t' called 'initial_stack_size'.
	Also, to prevent dynamic allocation of the stack, and
	to instead assign a fixed initial address to the stack pointer,
	a global pointer called 'initial_stack_top' can be declared, which
	contains this value. 
 
------------------------------------------------------------------------------
The BootParam interface:

    The 'bootparam' pointer points to a structure with the following
    members:	

	-------------------------------------------------------------------
	Portable interface members:

	uint8   bootver_major;
        uint8   bootver_minor;
        char    *bootname;
			These are the major and minor numbers of the boot
			loader version, and the name of the boot
			loader.  For the current release, they
			ought to be 0, and 96, and "solo", respectively.
			They can be used together to determine the 
			type of interface available for compatibility.

	
        int32   argc;
        char    **argv;
			These are the 'argc' and 'argv' values to be passed
			to the main program, and represent the total number
			of arguments (including the name of the executable),
			and an array of the arguments (with argv[0] being
			the name of the executable)

        char    **envp;
			This is a pointer to an array of environment values,
			which are currently unused, due to the internal
			format of SOLO's environment needing a complete
			overhaul first.

        uint32  entry;
			This is the starting execution address of the
			program that is currently running.

        uint32  lomem;
        uint32  himem;
			These represent the lowest and highest available
			virtual memory addresses.  LibSolo uses these to
			set up the heap for malloc() and free().   Himem
			is actually one greater than the highest useable
			address, so that (himem-lomem) actually gives the
			available size.

        PSeg    text;           /* text segment */
        PSeg    data;           /* data segment */
        PSeg    spare;          /* spare segment */
        PSeg    bss;            /* bss segment */
			These give details about the program segments
			which were loaded for the current executable, such	
			as the virtual and physical addresses used, etc.

        uint32  msgbuf;         /* kernel message buffer */
        uint16  msgsize;        /* size of message buffer */
        uint16  msgptr;         /* current pointer into message */
			These are used for the circular message buffer.
			All important kernel information should be put
			in this buffer, so it is available if the system
			panics.  Writing through the standard console
			device automatically adds the output to this buffer
			if the 'msg' flag is turned on in SOLO.

        fd_t    console_fd;     /* file descriptor for console */
        fd_t    self_fd;        /* file descriptor for current executable */
			These provide the file descriptors for the 
			console, and current executable, respectively, and
			can be used with the file functions below to
			access these devices or files.

        void    exit(int32 code);
			This function will exit from the kernel or 
			standalone program with the specified exit code.
 
        void    reboot(bool cold);
			This function will perform a warm or cold
			reboot of the system. (true = cold reboot)

        fd_t    open(const char *name);
			This function will open the specified file
			(the full path name must match exactly... there
			is no true notion of directories, so that flexible
			file specification formats are allowed)
			and return the file descriptor, or 0 if the 
			file could not be opened.

        void    close(fd_t);
			This closes the specified file.

        const MapFile* readdir(index_t);
			This function takes an index into the file map,
			and returns a pointer to the corresponding map entry,
			or NULL (0) after the last entry is reached.
			
			Each MapFile entry contains the following members:
				char name[];	null terminated name
				uint namelen;	length of the name
				uint fsize;	file size in bytes
				uint nchunks;	file size in chunks
				uint device;	device type
				uint unit;	device unit and flags
				int mtime_hi;	high 32 bits of 64 bit
						nanosecond time of file	
						(since Jan 1, 1970 00:00:00)

        int32   read(fd_t, void *buffer, uint32 len);
			Try to read the specified number of bytes from
			the file, into the buffer, and return the number
			actually read, or 0 for EOF, or -1 for error

        int32   write(fd_t, void *buffer, uint32 len);
			Try to write the specified number of bytes to
			the file, from the buffer, and return the number
			actually written, or -1 for error

        void    fstat(fd_t, FileInfo *stbuf);
			Get status on an open file or device, and put
			the information in a FileInfo buffer, consisting
			of the following:
				uint64 ctime;	creation time
				uint64 mtime;	last modification time
				uint64 atime;	last access time
				uint	size;	total size of file or device
				uint	blocks;	total number of blocks
				uint	blksize; size of blocks
				uint	rdev;	raw device ID

				/* only applicable to disk devices, and
				   possibly moving elsewhere in the future: */
				uint	cylinders;	# of cylinders
				uint	heads;		# of heads
				uint	sectors;	# of sectors

        void    seek(fd_t, uint32 of);
			Seek to the specified position in the file.

	uint32	tell(fd_t);
			Tell the current position in the file.


	-------------------------------------------------------------------
	ix86 PC and PC BIOS interface members:

	CpuInfo* cpuinfo;
			pointer to CPU information, described in 'cpuinfo.h'

        uint32  buffer;		/* buffer address */
        uint16  bufsize;        /* size of buffer */
			this provides the address and size of a buffer
			in base memory that can be safely used for BIOS calls

        uint8   bootdev; 
			this is the BIOS code for the boot device, such
			as 00 for floppy disk 0, 0x80 for hard drive 0,
			etc...

        uint32  base_addr;      /* minimum base address */
        uint32  base_size;      /* size of base memory */
			These describe the portion of base memory that
			can be used without interfering with memory used
			by SOLO.  Of course, without SOLO, the region
			from 0 to base_addr can be reclaimed.
			
        uint32  base_lomem;     /* lowest available base address */
        uint32  base_himem;     /* highest available base address + 1 */
			These describe the bounds of base memory still	
			available without after loading the current
			program.  Writing to memory between base_addr
			and base_lomem will probably write over the current
			program, but of course, if the program relocates
			itself somehow (why it would need to,
			I really don't know, since it is already mapped
			virtually where it wants to be)
			it can reuse this memory.

        uint32  ext_addr;       /* minimum extended memory address */
        uint32  ext_size;       /* size of available extended memory */
			These describe the location and size of all
			detected extended memory.  It is likely that
			SOLO will not yet detect memory sizes above
			32MB, (since I've been told the BIOS won't
			detect sizes above that-- I have no machine
			to test this on though)  but in the future
			that will be fixed, so that this should represent
			an accurate total count of bytes.
 
        uint16  timer0_count;   /* timer0 value */
        uint8   timer0_mode;    /* timer0 mode */
			These values must be set to the count and mode
			of Programmable Interval Timer 0, if those values
			are ever changed, so that they can be properly
			restored after calls to the BIOS.  

        void    panic(uint8 type, uint32 num, uint32 code, TssBlock *tss);
			This function can be called from a kernel panic
			routine, with the type of panic, the number
			for that type, an extra code, and a TSS block
			which describes all of the register states at
			the time.  You do not need to use this 
			Panic types are:
				PANIC_TYPE_IRQ		/* interrupt request */
				PANIC_TYPE_EXC		/* exception */
				PANIC_TYPE_RESET	/* CPU reset */
				PANIC_TYPE_OTHER	/* all other types */
			
			The panic call itself should exist on all platforms,
			but the parameters to it are platform specific.

        void    callbios(uint8 vec, RegBlock *reg);
			This calls a BIOS interrupt, using the supplied
			vector, and a pointer to a RegBlock (see regblock.h)
			which describes all the registers before and 
			after the call.
			

	It is important to note that this structure is only available
	so long as the lower 32K of physical memory is still mapped
	to the same corresponding virtual addresses...
	Once your kernel remaps memory, it must explicitly map this
	memory back in any time that part of this structure is accessed.
	Also, in order to call functions through the bootparam 
	structure, the lower 1MB of physical memory should be mapped
	to corresponding virtual addresses for the duration of the call.
 
------------------------------------------------------------------------------
LibSolo:

	To use LibSolo, include 'libsolo.h', and link with '-lsolo'.
	LibSolo then provides the functions
		exit(), gets(), puts(), vprintf(), printf(),
		open(), close(), read(), write(), lseek(),
		calloc(), malloc(), realloc(), free()
	
	You can define SOLO_NOSTDIO before including 'libsolo.h'
	to only declare standard I/O functions with the 'solo_'
	prefix.  This can prevent namespace clashes.  Otherwise, it declares
	many functions with standard names, like including <stdio.h> would.
	You can also define SOLO_NOSTDMM to declare memory managment
	routines with only the 'solo_' prefix.

	See 'libsolo.h' for more details.

------------------------------------------------------------------------------
Writing standalone programs:

	For some sample programs, see 'fdisk.C', 'hello.C', and 'solotest.C'

------------------------------------------------------------------------------
Creating a new work area under the source tree:

	There's no reason you should have to use the unusual build
	area that is used to create SOLO and Shag/OS, but if you do wish
	to make your own work area that is part of the source tree,
	here's how to do it: (if not, go to the next section)

	 - create a new directory under the source tree wherever it seems
	 	appropriate.

	 - put appropriate source files in your new directory

	 - update the 'SUBDIRS=' line in the 'Make' file in the parent
		directory to include your new subdirectory

	 - copy 'Make.gen' from the main source directory to your new 
		directory, and call it 'Make'

	 - edit it to fill in source and include files that you need
		For instance, for myprog.c, you might have:
			SRCFILES += myprog.c

	 - add targets you wish to build.  Add an entry to 'SFILES'
		for standalone programs, or 'STFILES' for standalone
		test programs. (using 'emsolo')
		For instance, to make standalone and test programs for
		'myprog', add:
			SFILES += myprog.x
			STFILES += myprog

	 - add an entry such as the following to build 'myprog.x':
		(using '.x' for standalone executables helps to distinguish
		 them from ones you can run under the local OS)
			myprog.x_OBJS += myprog.o
			myprog.x_LIBS += -lsolo -lstdc
			myprog.x: $(myprog.x_OBJS)
		Optionally, you can also add other flags for linking,
		such as the following to relocate the executable 
		to load at a specified address, in this case '0xf0000000':
			myprog.x_FLAGS += -Wl,-Ttext,0xf0000000
		(The '-Wl,xxx,xxx' syntax is because this is the only
		way to get gcc to pass non-standard options through
		to the linker)

	- if you want to build 'myprog' also (the local test version)
		just also add:
			myprog: $(myprog.x_OBJS)

	- now go into your build area, to the parent directory of your
		new directory, and just type:
			make new_directory_name/
		(where "new_directory_name" is the name of your new directory)
	 	This will create the new build directory, and configure it.
		If you make changes to the Make file, then just typing
		'make config' in the new directory will fix it for those
		changes.  If, for some reason, things get really messed up,
		just delete the build directory and make it again.
		
	- now build 'myprog' and 'myprog.x' by typing:
		make myprog myprog.x
	
	- you can now test the local version of 'myprog' by just running it:
		./myprog
	
	- You can even pass command line options to it, if your program
		uses them.

------------------------------------------------------------------------------
Using your own independent work area:

	If, on the other hand, you wish to develop in a more familiar
	environment, just make sure you add '-L' and '-I' options to
	your Makefile to include the 'include' and 'lib' directories from
	the SOLO build area.

	For instance, if your build area is in /usr/local/sos/build,
	you might use the following command to compile a file
	that uses LibSolo:
	
		gcc -c -I/usr/local/sos/build/include mytest.C
	
	And then to link it:
	(Due to the number of options needed for this step, it's
	probably best to put it in a Makefile or shell script)

		gcc -L/usr/local/sos/build/lib -nostartfiles \
			-Wl,-Ttext,0x10000000 \
			-o mytest \
			/usr/local/sos/build/lib/srt0.o \
			/usr/local/sos/build/lib/crt_head.o \
			mytest.o \
			-nostdlib -lsolo -lstdc -lgcc \
			/usr/local/sos/build/lib/crt_tail.o 

	(the '-Wl,Ttext,0x10000000' option tells gcc to pass the
	'-Ttext 0x10000000' options through to the linker, causing it
	to relocate the executable to address 0x10000000.  You can pick
	any address to relocate to, as long as it's larger than 0x000fffff,
	and smaller than 0xffffffff minus the total size of your
	executable, and all memory needed by it.  I find 0x10000000 is
	good for all general purpose programs, and 0xf0000000 is good
	for kernels, but your preferences may vary)

	Or to link a local test version:  (to run locally)
		gcc -L/usr/local/sos/build/lib -o mytest mytest.o \
			-lsolo -lemsolo
	
	The '-lsolo' part is optional, depending on whether or not you
	wish to use the LibSolo interface library.

	Of course, you could omit the '-nostdlib' also, and use the
	standard libraries, but be careful if you do--- many parts of them
	will contain references to functions that are not available when
	your standalone program is running.

	Note:
	If, after you build SOLO and LibSOLO, you want to clean up
	the directories so that they just have the libraries, executables,
	and header files, 'make cleantemp' will clean up an individual
	directory, and 'make cleantemp_all' will recurse into the
	tree and clean up all directories.  This will save a significant
	amount of disk space.

------------------------------------------------------------------------------
Kernel development:

    Developing a kernel can start out as simply as developing
    a standalone program with SOLO.  However, as the kernel grows
    more complex, some issues have to be noted, such as the following:

    
        -------------------------------------------------------------------

    	Simple standalone programs can just use LibSolo for their
	functionality.  More advanced systems, such as kernels, might want
	to use LibSolo during early development, but after such features
	as multi-tasking and memory management are added to the kernel,
	it is far better to use the BootParam interface directly. 
	This is mainly because the low 32K of memory needs to stay mapped
	in for the calls used by LibSolo to work, and this cannot
	always be guaranteed.  So, in order to work correctly, you must
	make sure that the low 32K is mapped in before any references
	to the BootParam structure, and when you're done, you can map 
	it out again.
	
	This can be accomplished in C++ by using a Class whose constructor
	maps in the memory, and whose destructor maps it out again.  Then,
	if operator* and operator-> are defined to return pointers to
	the BootParam structure, it can be used as simply as this:

			void puts(const char *s)
			{
				BootParamPtr bootparam;
				bootparam->write(bootparam->console_fd, 
						s, strlen(s));
			}
				
	The constructor and destructor will automatically do all the
	behind-the-scenes work of mapping the memory, if they are written
	correctly.

        -------------------------------------------------------------------

	Base memory (base_addr, base_size) represents total available
	base memory, including that already consumed by the currently
	running program and its page tables.  
	Notice that it does not include the part of memory used
	by the BIOS and the loader. (currently the lower 32K)
	If your program will never exit or call BIOS services, then
	it can reclaim this memory by just adding base_addr to
	base_size, and setting base_addr to 0.  This is not recommended,
	because all SOLO functionality is then lost, including the 
	ability call the BIOS or handle traps, so be sure your kernel
	is completely independent and will never exit before you do this.

        -------------------------------------------------------------------

	Make no assumptions about the mappings of virtual 
	to physical memory.  Make sure you use the page tables
	to look up all mappings for each page, especially 
	since the ELF executable format may cause some
	unusual mappings.  Some parts may be sequential, but there is
	no guarantee, so look up each page separately.

        -------------------------------------------------------------------

	Since all page tables are within the range base_addr to base_addr
	+base_size-1, and the lower 1Mb of memory is already mapped
	at the same corresponding addresses,
	you can look up physical addresses for page tables directly,
	which makes translating of the addresses easier.
	Take advantage of this for simplicity when starting up,
	but remember that later on your memory management scheme
	may not always map new page tables to the lower part of memory.

