460 Lab Assignment #2

                Lab Assignment #2 : loader
                    DUE : to be announced
                              
A simple extension of the MTX booter is a loader, which loads an a.out file
into memory for execution. A loader is an essential (utility) function of an
operating system.

1. a.out file format:
   Assume bs.s and bc.c are assembly and C source files. Under BCC, 

         ld86 -s bs.o bc.o /usr/lib/bcc/lic.a

   generates an a.out file consisting of the following parts.

       |header|Code|Data|...bss.....|

   where bss is NOT in a.out but its size is recorded in the header.

   The 32-byte header is composed of 8 longs.

        0 0x04100301 //(combined I,D space) or 0x04200301=(separate I,D space)
        1 0x00000020
        2 text size  // code size         
        3 data size  // initialized data size
        4 bss  size  // uninitialized data size ==> cleared to 0
        5 0x00000000
        6 total memory      // ignore this field
        7 symbolTable size  // symbol Table size

   If a.out has a combined I (Instruction) and D (Data) space, its code segment
   and data+stack segment are all the same, i.e. CS,DS,and SS must be the same 
   segment during execution. This also means that the entire memory image must
   be no greater than 64KB. ld86 -i generates a.out with separate I and D 
   spaces, in which case the code segment and data+stack segment will be 
   different, allowing a memory image of 128KB (64KB code segment and 64KB
   data+stack segment).

2. The loader
   To execute a.out, the loader first reads the file header to determine

      totalMemorySize = tsize + dsize + bsize + (initial)stack_size

   where the initial stack_size can be a default value, say 8KB, assuming
   it can be expanded (if necessary) during execution. If the memory image of 
   a loaded a.out cannot be changed, then a max. default stack_size is usually
   used. Then the loader allocates the needed memory space by

        ushort segment = OSmalloc(totalMemorySize);

   where OSmalloc() is the memroy manager of the OS.

   Then, the loader loads the |code|data| of a.out into memory (starting at 
   segment) and clears the bss section to 0 (so that all the uninitialized 
   globals start with 0 value). Finally, the OS sends CPU to segment to execute
   a.out.

3. LAB2. Specification:
   Implement a loader that loads an a.out file to memory and execute it.

4. HELP Files:
4-1. YOUR MTX booter as in Lab #1. It boots /boot/mtx to the segment 0x1000.

4-2. Instead of the real MTX, we shall use a S(Simple)OS generated by the 
following ts.s and tc.c files. 

-------------------------------------------------
bcc  -c tc.c
as86 -o ts.o ts.s
ld86 -d ts.o tc.o /usr/lib/bcc/lic.a
mount /dev/fd0 /fd0
cp a.out /fd0/boot/sos   (you may name it mtx)
umount /fd0
-------------------------------------------------

(1). ts.s file of SOS:

.globl begtext, begdata, begbss                      ! needed by linker

.globl _getc,_putc,_diskr,_setes,_inces,_error       ! EXPORT
.globl _get_byte,_put_byte, _go                      ! EXPORT
.globl _main,_prints                                 ! IMPORT these 

.text                                                ! these tell as:	
begtext:                                             ! text,data,bss segments
.data                                                ! are all the same.
begdata:
.bss
begbss:
.text 

start:
        mov     ax,cs                   ! set segment registers for CPU
        mov     ds,ax                   ! we know CS=0x1000. Let DS=ES=CS  
        mov     es,ax                   ! ES = CS
        mov     ss,ax                   ! SS = CS ===> all point at 0x1000

        mov     sp,#0                   ! SP = 64KB,    at 0x2000

        call  _main                     ! call main() in C

dead:	jmp   dead
	 

       !---------------------------------------
       ! diskr(cyl, head, sector, buf)  all count from 0
       !        4     6     8      10
       !---------------------------------------
_diskr:                             
        push  bp
	mov   bp,sp            ! bp = stack frame pointer

        movb  dl, #0x00        ! drive 0=FD0
        movb  dh, 6[bp]        ! head
        movb  cl, 8[bp]        ! sector
        incb  cl               ! inc sector by 1 to suit BIOS
        movb  ch, 4[bp]        ! cyl
        mov   bx, 10[bp]       ! BX=buf ==> memory addr=(ES,BX)
        mov   ax, #0x0202      ! READ 2 sectors to (EX, BX)
	
        int  0x13              ! call BIOS to read the block 
        jb   _error            ! to error if CarryBit is on [read failed]

        pop  bp                
	ret

        !------------------------------
        !       error & reboot
        !------------------------------
_error:
        mov  bx, #bad
        push bx
        call _prints
        
        int  0x19                       ! reboot

bad:    .asciz  "Error!\n\r"

!======================== I/O functions =================================
		
        !---------------------------------------------
        !  char getc()   function: returns a char
        !---------------------------------------------
_getc:
        xorb   ah,ah           ! clear ah
        int    0x16            ! call BIOS to get a char in AX
        ret 

        !----------------------------------------------
        ! void putc(char c)  function: print a char
        !----------------------------------------------
_putc:           
        push   bp
	mov    bp,sp
	
        movb   al,4[bp]        ! get the char into aL
        movb   ah,#14          ! aH = 14
        movb   bl,#0x0B        ! bL = CYAN color 
        int    0x10            ! call BIOS to display the char

        pop    bp
	ret
        
_setes:  push  bp
	 mov   bp,sp

         mov   ax,4[bp]        
         mov   es,ax

	 pop   bp
	 ret
	
_inces:                         ! inces() inc ES by 0x40, or 1K
         mov   ax,es
         add   ax,#0x40
         mov   es,ax
         ret

_go:  ! go(segment)
        push   bp
        mov    sp,bp
        mov    ax,4[bp]
        mov    segment,ax



! jmpi   0, segment

opcode:    .byte  0xEA     ! jmpi
           .word  0        ! offset
segment:   .word  0        ! segment
	

!*===========================================================================*
!*				get_byte				     *
!*===========================================================================*
! c = get_byte(segment, offset)
_get_byte:
	push bp			! save bp
	mov bp,sp		! stack frame pointer
	push es			! save es

	mov es,4[bp]		! load es with segment value
	mov bx,6[bp]		! load bx with offset from segment
	seg es			! go get the byte
	movb al,[bx]		! al = byte
	xorb ah,ah		! ax = byte

	pop es			! restore es
	pop bp			! restore bp
	ret			! return to caller

!*===========================================================================*
!*				put_byte				     *
!*===========================================================================*
! put_byte(char,segment,offset)
_put_byte:
	push bp			! save bp
	mov  bp,sp		! stack frame pointer
	push es			! save es
        push bx

	mov  es,6[bp]   	! load es with seg value
	mov  bx,8[bp]		! load bx with offset from segment
        movb al,4[bp]           ! load byte in aL
	seg  es			! go put the byte to [ES, BX]
	movb [bx],al		! there it goes

        pop  bx                 ! restore bx
	pop  es			! restore es
	pop  bp			! restore bp
	ret			! return to caller




(2). tc.c file of SOS:

#include "ext2.h"           // EXT2 INODE, DIR struct types
#include "io.c"             // YOUR printf() with "%c %s %d %x %l\n";
#include "loader.c"         // YOUR loader.c file

char filename[64];
ushort segment;

main()
{
  printf("\nSimple OS running\n");
  while(1){
    printf("enter filename to execute : ");
    gets(filename);
    segment = 0x2000;  // choose a free segment
    if (load(filename, segment)){
      printf("jmp to segment %s\n", segment\n");
      go(segment);
    }
    else{
      printf("exec ERROR : file not found\n");
    }
  }
}



(3). The executable file /bin/u1 is generated by
--------------------------------------------------
bcc  -c uc.c
as86 -o us.o us.s
ld86 -s us.o uc.o /usr/lib/bcc/libc.a
mount /dev/fd0 /fd0
cp a.out /fd0/bin/u1
umount /fd0
--------------------------------------------------

3-1. us.s file

.globl begtext, begdata, begbss                      ! needed by linker

.globl _getc,_putc                                   ! EXPORT
.globl _main,_prints                                 ! IMPORT these 

.text                                                ! these tell as:	
begtext:                                             ! text,data,bss segments
.data                                                ! are all the same.
begdata:
.bss
begbss:
.text 

start:
        mov     ax,cs                   ! set segment registers for CPU
        mov     ds,ax                   ! we know CS=segment. Let DS=SS
        mov     es,ax                   ! ES = CS
        mov     ss,ax                   ! SS = CS ===> all point at segment
        mov     sp,#0                   ! SP = 64KB

        call _main                      ! call main() in C

gomtx:	jmpi   0, 0x1000                ! jmp back to SOS at 0x1000
	
		
        !---------------------------------------------
        !  char getc()   function: returns a char
        !---------------------------------------------
_getc:
        xorb   ah,ah           ! clear ah
        int    0x16            ! call BIOS to get a char in AX
        ret 

        !----------------------------------------------
        ! void putc(char c)  function: print a char
        !----------------------------------------------
_putc:           
        push   bp
	mov    bp,sp
	
        movb   al,4[bp]        ! get the char into aL
        movb   ah,#14          ! aH = 14
        movb   bl,#0x0C        ! bL = RED color 
        int    0x10            ! call BIOS to display the char

        pop    bp
	ret
	

3-2 uc.c file

#include "io.c"   // YOUR gets(), printf(), etc.

main()
{
  char name[64];
  ushort segment;

  printf("\nThis is u1 running\n");
  while(1){
    printf("what's your name? : ");
    gets(name);
    if (strcmp(name, "quit")==0){
       printf("quit : go back to OS at 0x1000\n"); 
       return;
    }
    printf("Welcome %s\n", name);

  }
}

===========================================================================
In ~samples/LAB2/ sosimage.gz is a diskimage of a bootable sos system.
Download and dump it to a floppy disk. Boot up SOS and try to run it.
You only need to replace the booter with YOUR booter from Lab#1
AND generate YOUR SOS with YOUR loader.c and io.c files.
===========================================================================
Programming Tasks:

1. Develop your own printf(fmt) function, which can print with
           fmt = "....%c...%s ...%d ... %x ...%l ...\n"

2. Use get_byte()/put_byte() to implement YOUR own
 
      ushort get_word(segment, offset) ushort segment, offset;
      int    put_word(word, segment, offset) ushort word,segment,offset;

   functions in C for read/write a word from/to memory (segment,offset).


3. Implement YOUR loader.c
       
   load(pathname, segment) char *pathname; ushort segment;
   {
      1. break up pathname into tokens:
         /a/b/c/d  ==> "a", "b", "c", "d" with n=4
 
      2. locate the inode of /a/b/c/d: let ip->INODE of file 
         return 0 if can't find the file

      3. read file header to get size information;
         print tsize, dsize, bsize

      3. load a.out data blocks (with header) into segment.
         move the executable part (|code|dat|....) upward 32 bytes to
         eliminate the header.

      4. Clear bss section (after |code|data|) to 0.

      5. return 1 for success.